// C++ Templates: 3.1 非类型的类模板参数
// 基本概念
// 非类型模板参数（Non-type template parameters）是指在模板参数列表中使用的具体值，而不是类型。
// 这些参数在编译时必须是常量表达式，因为模板实例化是在编译期进行的。

// 语法和使用
// 非类型模板参数的基本语法如下：
template <typename T, int Size>
class Array
{
private:
    T elements[Size];

public:
    // ...
};
// 在这个例子中，T是类型参数，而Size是非类型参数。

// 每次使用不同的Size值，编译器都会生成一个新的类模板实例。
// 允许的非类型参数类型
// 在C++17及之前，非类型模板参数可以是以下类型：
//      1.整型类型（如int、long、char等）
//      2.枚举类型
//      3.指针类型（包括函数指针和成员指针）
//      4.左值引用类型
//      5.std::nullptr_t
// C++20进一步扩展了允许的类型，包括：
//      6.浮点类型（如float、double）
//      7.字面量类（满足特定条件的类）

// 限制和规则
// 1.必须是常量表达式：非类型参数必须在编译时确定
int size = 10;
Array<int, size> arr; // 可以，如果size是constexpr或const

int getSize();
Array<int, getSize()> arr2; // 错误：getSize()不是常量表达式

// 2.不能使用字符串字面量或全局对象的地址
template <const char *str>
class StringTemplate
{
};

StringTemplate<"hello"> s; // 错误：不能使用字符串字面量

// 3.不能使用浮点数（C++20之前）
template <double Value> // C++20之前错误，C++20开始允许
class DoubleTemplate
{
};

// 实际应用示例
// 1. 固定大小的数组
template <typename T, size_t Size>
class FixedArray
{
private:
    T data[Size];

public:
    T &operator[](size_t index)
    {
        return data[index];
    }

    const T &operator[](size_t index) const
    {
        return data[index];
    }

    size_t size() const
    {
        return Size;
    }
};

// 使用
FixedArray<int, 5> arr;
arr[0] = 10;

// 2. 编译期计算
template <int N>
struct Factorial
{
    static constexpr int value = N * Factorial<N - 1>::value;
};

template <>
struct Factorial<0>
{
    static constexpr int value = 1;
};

// 使用
constexpr int fact5 = Factorial<5>::value; // 120

// 3. 策略模式
// 策略类
struct FastAlgorithm
{
    static void process() { /* 快速但不精确的实现 */ }
};

struct AccurateAlgorithm
{
    static void process() { /* 精确但较慢的实现 */ }
};

// 使用策略的类模板
template <typename T, typename Strategy>
class Processor
{
public:
    void process(T value)
    {
        // 预处理...
        Strategy::process();
        // 后处理...
    }
};

// 使用
Processor<int, FastAlgorithm> fastProcessor;
Processor<int, AccurateAlgorithm> accurateProcessor;

// 4. 编译期条件选择
template <typename T, bool UseChecking>
class Container
{
public:
    void add(const T &element)
    {
        if constexpr (UseChecking)
        {
            // 执行边界检查
            if (isFull())
                throw std::overflow_error("Container is full");
        }
        // 添加元素...
    }

    bool isFull() const;
};

// 使用
Container<int, true> safeContainer;  // 带检查
Container<int, false> fastContainer; // 不检查，更快

// **与默认模板参数结合
// 非类型模板参数也可以有默认值：
template <typename T, size_t Size = 10>
class Buffer
{
private:
    T data[Size];

public:
    // ...
};

// 使用默认大小
Buffer<int> defaultBuffer; // Size = 10

// 指定大小
Buffer<int, 100> largeBuffer; // Size = 100

// **非类型参数的推导
// C++17引入的类模板参数推导也适用于非类型参数：
template <typename T, size_t Size>
class StaticArray
{
    T data[Size];

public:
    StaticArray(const T (&arr)[Size])
    {
        std::copy(arr, arr + Size, data);
    }
    // ...
};

// C++17之前
int arr[] = {1, 2, 3, 4, 5};
StaticArray<int, 5> sa(arr);

// C++17及以后
StaticArray sa2(arr); // 自动推导为StaticArray<int, 5>

// C++20的改进
// C++20允许使用浮点类型和满足特定条件的类作为非类型模板参数：
// 浮点类型参数
template <double Value>
class DoubleConstant
{
public:
    static constexpr double value = Value;
};

DoubleConstant<3.14159> pi;

// 字面量类型参数
struct Point
{
    int x;
    int y;

    constexpr Point(int x, int y) : x(x), y(y) {}

    friend constexpr bool operator==(const Point &, const Point &) = default;
};

template <Point P>
class PointTemplate
{
public:
    static constexpr Point value = P;
};

PointTemplate<Point{1, 2}> pt;

// 总结
// 非类型模板参数是C++模板系统的强大特性，它允许我们在编译期使用具体值来参数化类模板。这种特性在需要编译期确定大小、策略或行为的场景中特别有用，可以帮助我们编写更灵活、更高效的代码。

// 随着C++标准的发展，非类型模板参数的能力不断增强，特别是C++20引入的对浮点类型和字面量类的支持，进一步扩展了其应用范围。